import { useAuthStore } from '@/store/auth'
import { useEnvStore } from '../../store/env'
import store from '@/store'
import type { OemMessage, WS } from './types'
import { SocketReadyState, WebsocketUrls } from '@/utils/socket/constants'
import { usePageEffect, type PageEffectOptions } from '../composition/usePageEffect'
import { httpRequestErrorHandle } from '@/httpRequest/helper/errorHelper'
import type { ErrorResponse } from '../request'
import { actionDebounce } from '../mpHelper'
import { reportEventData } from '@/dataReport/dataReportUtil'
import keyUtils from '@/dataReport/keyUtils'
let logger = wx.getLogManager({
  level: 1,
})
const envStore = useEnvStore(store)
const authStore = useAuthStore(store)

/**
 * 监听WebSocket 根据条件自动清除监听器
 * @param websocket WebSocket实例
 * @param listener 监听方法【返回布尔值 表示是否需要清除当前监听 下次不再触发】
 * @param options 配置选项
 */
export function useWebsocketListener(
  websocket: WebsocketHandlerImp,
  listener: WS.MessageListener,
  options?: PageEffectOptions
) {
  return usePageEffect(
    {
      onInvoke: () => {
        console.log('onInvoke.....')
        websocket.addMessageListener(listener)
        websocket.setRetryCount = 0
      },
      onUnload: () => {
        console.log('onUnload.....')
        websocket.removeMessageListener(listener)
      },
    },
    options
  )
}

export class WebsocketHandlerImp {
  private SocketTask!: UniNamespace.SocketTask | null

  private token = ''
  private url = ''
  private retryCount = 0 // socket异常断开重试三次
  private inhouseInternal = 0
  private oemInternal = 0

  private listeners = new Set<WS.MessageListener>() // 监听器队列
  private socketReadyState: SocketReadyState = SocketReadyState.CLOSED

  get isInhouse() {
    return WebsocketUrls.Inhouse.includes(this.url)
  }
  get isOem() {
    return WebsocketUrls.Oem.includes(this.url)
  }
  get isConnectSocket() {
    return this.socketReadyState === SocketReadyState.OPEN
  }
  set setRetryCount(retryCount: number) {
    this.retryCount = retryCount
  }
  constructor(url: string) {
    console.info('WebSocketHandlerImp ... constructor ')
    logger.info('WebSocketHandlerImp ... constructor ')
    this.url = url
  }

  /**
   * 创建WebSocket连接
   */
  createConnection = actionDebounce({
    call: () => this.baseCreateConnection(),
  })
  private async baseCreateConnection() {
    const noToken = authStore.accessToken === ''
    if (noToken) {
      console.warn('Websocket connect failed caused by token does not exist.')
      logger.info('Websocket connect failed caused by token does not exist. ')
      return
    }

    if (this.socketReadyState !== SocketReadyState.CLOSED) {
      await this.closeConnection()
    }

    this.token = this.isInhouse ? `Bearer ${authStore.accessToken}` : authStore.accessToken
    const methodType = 'GET'

    return new Promise<void>((resolve) => {
      console.log('createConnection', 'url', this.url)
      this.SocketTask = uni.connectSocket({
        url: this.url,
        header: {
          'content-type': 'application/json',
          Authorization: this.token,
          'X-FkMapping': 'false',
        },
        method: methodType,
        success: (res) => {
          console.info(`${this.isInhouse ? 'isInhouse' : 'isOem'}  connectSocket`, 'success', res)
          logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'}   connectSocket`, 'success', res)
          this.socketReadyState = SocketReadyState.CONNECTING
        },

        fail: (err) => {
          console.error(`${this.isInhouse ? 'isInhouse' : 'isOem'}  connectSocket`, 'fail', err)
          logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'}    connectSocket`, 'fail', err)
        },
        complete: () => {
          resolve()
        },
      })
      this.setOnSocketOpenListener()
    })
  }

  /**
   * 关闭webSocket
   */
  async closeConnection() {
    // 关闭Websocket
    await new Promise<void>((resolve) => {
      if (!this.SocketTask) return resolve()

      this.SocketTask.close({
        fail: () => {
          this.SocketTask = null
        },
        complete: (res) => {
          console.info(`${this.isInhouse ? 'isInhouse' : 'isOem'}   closeConnection`, res)
          logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'}    closeConnection`, res)
          this.socketReadyState = SocketReadyState.CLOSING
          resolve()
        },
      })
    })

    // 等待关闭成功
    await new Promise<void>((resolve) => {
      if (!this.SocketTask) return resolve()

      this.SocketTask.onClose((res) => {
        console.log(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketCloseListener`, res)
        logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketCloseListener`, res)
        this.socketReadyState = SocketReadyState.CLOSED

        // 清除心跳定时器
        if (this.isInhouse) {
          clearInterval(this.inhouseInternal)
        }
        if (this.isOem) {
          clearInterval(this.oemInternal)
        }
        this.SocketTask = null
        resolve()
      })
    })
  }

  /**
   * 消息监听器
   */
  addMessageListener(listener: WS.MessageListener): void {
    this.listeners.add(listener)
  }
  removeMessageListener(listener: WS.MessageListener): void {
    this.listeners.delete(listener)
  }

  /**
   * 发送websocket
   * @param content
   * @param success
   * @param fail
   */
  sendMessage(
    content: object | string,
    onComplete?: (res: UniNamespace.GeneralCallbackResult) => any
  ): Promise<UniNamespace.GeneralCallbackResult> {
    console.log('sendMessage', content)
    return new Promise((resolve, reject) => {
      if (this.socketReadyState === SocketReadyState.OPEN) {
        this.SocketTask?.send({
          data: JSON.stringify(content),
          success: (res) => resolve(res),
          fail: (err) => reject(err),
          complete: (res) => onComplete && onComplete(res),
        })
      }
    })
  }

  private setOnMessageListener() {
    const isInhouse = this.isInhouse
    const isOem = this.isOem
    this.SocketTask?.onMessage((res) => {
      const msg = JSON.parse(res.data)
      console.info('WebsocketHandlerImp', `${isInhouse ? 'isInhouse' : 'isOem'}   onMessage`, msg)
      logger.info('WebsocketHandlerImp', `${isInhouse ? 'isInhouse' : 'isOem'}    onMessage`, msg)
      // 心跳无需处理
      if (msg === 'ping') return

      for (const listener of this.listeners) {
        if (isInhouse) {
          listener(msg as WS.Message)
        } else if (isOem) {
          const oemMsg = msg as OemMessage.Message
          if (oemMsg.code !== 0) {
            let errorReponse: ErrorResponse = {
              code: oemMsg.code,
              data: {
                key: '',
                description: '',
              },
              messageid: oemMsg.messageid,
              msg: oemMsg.msg,
            }
            // if (errorReponse.code == 910001) {
            //   return
            // }

            // httpRequestErrorHandle(errorReponse)
          } else {
            const { haid: haId, type, items, timestamp } = oemMsg.event
            listener({ haId, type, items, timestamp } as WS.Message)
          }
        }
      }
    })
  }

  /**
   * websocket onOpen 监听
   */
  private setOnSocketOpenListener() {
    this.SocketTask?.onOpen(() => {
      console.info(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketOpenListener...onOpen`, 'WebSocket连接已打开!')
      logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'}  setOnSocketOpenListener...onOpen`, 'WebSocket连接已打开!')
      this.socketReadyState = SocketReadyState.OPEN
      reportEventData(keyUtils.WEBSOCKET_RETRY_COUNT, {
        retryCount: this.retryCount,
        des: `${this.isInhouse ? 'isInhouse' : 'isOem'}Websocket`,
      })
      this.setPingTimer()
      this.setOnMessageListener()
      this.setOnSocketCloseListenser()
      this.setOnSocketErrorListener()
    })
  }

  private setOnSocketCloseListenser(): void {
    this.SocketTask?.onClose((res) => {
      console.log(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketCloseListenser`, res)
      logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketCloseListenser`, res)
      this.socketReadyState = SocketReadyState.CLOSED
      if (this.isInhouse) {
        clearInterval(this.inhouseInternal)
      }
      if (this.isOem) {
        clearInterval(this.oemInternal)
      }
      this.SocketTask = null
      logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketCloseListenser...retryCount`, this.retryCount)
      if (this.retryCount < 10 && res?.code !== 1000) {
        this.retryCount++
        this.createConnection()
      }
    })
  }

  private setOnSocketErrorListener(): void {
    this.SocketTask?.onError((res) => {
      console.log(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketErrorListener`, res)
      logger.info(`${this.isInhouse ? 'isInhouse' : 'isOem'} setOnSocketErrorListener`, res)
      this.socketReadyState = SocketReadyState.CLOSED
    })
  }
  setPingTimer(): void {
    if (this.isInhouse && this.socketReadyState === SocketReadyState.OPEN) {
      clearInterval(this.inhouseInternal)
      this.inhouseInternal = setInterval(() => {
        this.sendMessage('').then(
          (res) => {
            console.log('inhouse sendMessage...suc', res)
          },
          (err) => {
            console.log('inhouse sendMessage...fail', err)
            this.socketReadyState = SocketReadyState.CLOSED
          }
        )
      }, 55000)
    }
    if (this.isOem && this.socketReadyState === SocketReadyState.OPEN) {
      clearInterval(this.oemInternal)
      console.log('setPingTimer', 'isOem', this.isOem)
      this.oemInternal = setInterval(() => {
        this.sendMessage('ping').then(
          (res) => {
            console.log('oem sendMessage...suc', res)
          },
          (err) => {
            console.log('oem sendMessage...fail', err)
          }
        )
      }, 30000)
    }
  }
}
